using AOT;
using Unity.Burst;
using UnityEngine.Animations;
using UnityEngine.Playables;
using UnityEngine;
using System;
using AnimCollections;

namespace DA.AnimGraph {
    [BurstCompile]
    public unsafe partial struct SlotNode : INode {
        public struct SetupData : INodeSetupData {
            public AUID slotName;
            public float defaultDuration;
        }

        //
        AUID slotName;
        float defaultDuration;
        NativeSimpleArray<IntPtr> childrenGraphContext; // GraphContext*
        TransitionEvaluator transitionEvaluator;

        //
        private NodeHead head;
        private AnimationMixerPlayable playable;


        int nextPortIndex => 1 - currentPortIndex;
        int currentPortIndex;
        bool forceOnEnter; // 在执行 ReEnter 时，若原分支为激活无法自动走 OnEnter

        [BurstCompile]
        [MonoPInvokeCallback(typeof(NodeLink.DisposeDelegate))]
        private static bool Dispose(in void* node) {
            var self = (SlotNode*)node;

            // 不清理子节点，应当由它们原本的 graph 处理
            self->childrenGraphContext.Dispose();
            self->transitionEvaluator.Dispose();
            return false;
        }

        private void Setup(in SetupData data, ref GraphBuffer buffer, in NodeLink* link) {
            slotName = data.slotName;
            defaultDuration = data.defaultDuration;

            buffer.MallocNativeSimpleArray(out childrenGraphContext, 2);
            transitionEvaluator = new TransitionEvaluator(2, ref buffer);
        }

        [BurstCompile]
        [MonoPInvokeCallback(typeof(NodeLink.InitDelegate))]
        private static void Init(in void* node, in InitContext initContext) {
            var self = (SlotNode*)node;

            initContext.RegisterSlot(self->slotName, self);
        }

        private static Playable BuildPlayableGraph(in void* node, in NodeLink* link, in PlayableGraph graph, in BuildContext buildContext) {
            var self = (SlotNode*)node;

            self->head = new NodeHead(link);
            self->head.weights[0] = -1;
            self->head.weights[1] = -1;

            self->currentPortIndex = 1;

            self->playable = AnimationMixerPlayable.Create(graph, 2);
            return self->playable;
        }

        [BurstCompile]
        [MonoPInvokeCallback(typeof(NodeLink.EvaluateDelegate))]
        private static void Evaluate(in void* node, bool onEnter, in EvaluateContext evaluateContext, in GraphContext graphContext, ref UpstreamData upstream, ref ScopeContext scope) {
            var self = (SlotNode*)node;

            if (self->transitionEvaluator.count == 0) return;

            float deltaTime = evaluateContext.deltaTime;

            self->transitionEvaluator.Evaluate(deltaTime);
            self->transitionEvaluator.GetEnumerator(out TransitionEvaluator.Iter iter);
            while (iter.MoveNext()) {
                var (port, weight) = iter.current;
                var childScope = new ScopeContext(scope, weight);
                NodeLink* child = self->head.children[port];

                var currentIsNewState = iter.CurrentIsNewestState();
                if (currentIsNewState) {
                    if (self->forceOnEnter) {
                        childScope.forceOnEnter = true;
                        NodeLink.Evaluate(child, evaluateContext, *(GraphContext*)self->childrenGraphContext[port], ref upstream, ref childScope);
                    } else {
                        NodeLink.Evaluate(child, evaluateContext, *(GraphContext*)self->childrenGraphContext[port], ref upstream, ref childScope);
                    }
                } else {
                    childScope.disableBlackboardUpdate = true; // 旧分支不再更新参数，往往此时是不希望有切换的
                    NodeLink.Evaluate(child, evaluateContext, *(GraphContext*)self->childrenGraphContext[port], ref upstream, ref childScope);
                }

                self->head.weights[port] = weight;
                scope.Fusion(childScope);
            }
            self->forceOnEnter = false;

        }
        [BurstCompile]
        [MonoPInvokeCallback(typeof(NodeLink.GetTimeStateDelegate))]
        private static IntPtr GetTimeState(in void* node) {
            var self = (SlotNode*)node;

            if (self->transitionEvaluator.count == 0) {
                return default;
            } else {
                // 返回当前决定的状态，无论它权重多少
                var result = self->head.children[self->currentPortIndex]->GetTimeState();
                return (IntPtr)result;
            }
        }

        internal int Connect(in NodeLink* newLink) {
            return Connect(newLink, defaultDuration);
        }

        /// <returns>返回进行替换的端口</returns>
        internal int Connect(in NodeLink* newLink, float duration) {
            float newWeight = 0;
            int connectPort = nextPortIndex;

            bool result;
            if (transitionEvaluator.count == 0) {
                newWeight = 1;
                transitionEvaluator.SetFirstState(connectPort);
                result = true;
            } else {
                result = transitionEvaluator.AddNewState(connectPort, duration, false);
            }
            Debug.Assert(result, "why cannot add state");

            head.children[connectPort] = newLink;
            head.weights[connectPort] = newWeight;

            playable.DisconnectInput(connectPort);
            playable.ConnectInput(connectPort, newLink->playable, 0, newWeight);

            currentPortIndex = nextPortIndex;
            return connectPort;
        }

        /// <summary>
        /// 重新进入非当前的端口
        /// </summary>
        internal void ReEnter(int port) {
            if (currentPortIndex == port) {
                return;
            }
            bool result = transitionEvaluator.AddNewState(nextPortIndex, defaultDuration, false); // 暂且使用默认时间
            if (result) {
                currentPortIndex = nextPortIndex;
                forceOnEnter = true; // 防止此分支是有权重的，导致无法从默认状态开始
            }
        }

        internal void SetChildrenContext(int port, in GraphContext* graphContext) {
            childrenGraphContext[port] = new IntPtr(graphContext);
        }

        internal void Disconnect(int port) {
            if (head.children[port] == null) return;
            //if (children[port].link == null) return;

            if (port == nextPortIndex) {
                // 删除非当前，比较安全
                playable.DisconnectInput(port);
                //ref var child = ref children.GetRefAt(port);
                //child.link = null;
                head.children[port] = null;
                // todo:需要处理过渡（若在过渡中）,比如当前的直接设为1，终止过渡
            } else {
                // 删除当前的，但是也不该恢复成另一个
                playable.DisconnectInput(port);
                //ref var child = ref children.GetRefAt(port);
                //child.link = null;
                head.children[port] = null;
                // todo:处理过渡
            }
        }
    }
}
